package main

import (
	"encoding/json"
	"io"
	"log"
	"os"
	"sync"
)

type record struct {
	Key, Url string
}

type urlStore struct {
	urls map[string]string
	mu   sync.RWMutex
	save chan record
}

func NewUrlStore(filename string) *urlStore {
	u := &urlStore{urls: make(map[string]string),
		save: make(chan record)}
	if err := u.load(filename); err != nil {
		log.Fatalln("Load file error:", err)
	}
	go u.writeFile(filename)
	return u
}

func (u *urlStore) set(key, url string) bool {
	u.mu.Lock()
	defer u.mu.Unlock()
	if _, ok := u.urls[key]; ok {
		return false
	}
	u.urls[key] = url
	return true
}

func (u *urlStore) Get(key string) string {
	u.mu.RLock()
	defer u.mu.RUnlock()
	return u.urls[key]
}

func (u *urlStore) Put(url string) string {
	key := genKey(4)
	if ok := u.set(key, url); ok {
		u.save <- record{key, url}
		return key
	}
	panic("Put error")
}

func (u *urlStore) load(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		log.Println("Error open file:", err)
		return err
	}
	defer f.Close()
	d := json.NewDecoder(f)
	for err == nil {
		var r record
		if err = d.Decode(&r); err == nil {
			u.set(r.Key, r.Url)
		}
	}

	if err == io.EOF {
		return nil
	}
	return err
}

func (u *urlStore) writeFile(filename string) {
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatalln("Error Opening File For URLStore")
	}
	defer file.Close()
	encoder := json.NewEncoder(file)
	for {
		r := <-u.save
		if err := encoder.Encode(r); err != nil {
			log.Fatalln("Error save to file:", err)
		}
	}

}
